package com.atguigu.tingshu.album.service.impl;

import com.atguigu.tingshu.album.mapper.AlbumAttributeValueMapper;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.AlbumStatMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.service.AlbumAttributeValueService;
import com.atguigu.tingshu.album.service.AlbumInfoService;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.factory.ScheduleTaskThreadFactory;
import com.atguigu.tingshu.common.minio.service.FileOpsService;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.album.AlbumAttributeValue;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.AlbumStat;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.query.album.AlbumInfoQuery;
import com.atguigu.tingshu.vo.album.AlbumAttributeValueVo;
import com.atguigu.tingshu.vo.album.AlbumInfoVo;
import com.atguigu.tingshu.vo.album.AlbumListVo;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class AlbumInfoServiceImpl extends ServiceImpl<AlbumInfoMapper, AlbumInfo> implements AlbumInfoService {

    @Autowired
    private AlbumInfoMapper albumInfoMapper;
    @Autowired
    private AlbumAttributeValueMapper albumAttributeValueMapper;
    @Autowired
    private AlbumStatMapper albumStatMapper;
    @Autowired
    private TrackInfoMapper trackInfoMapper;
    @Autowired
    private AlbumAttributeValueService albumAttributeValueService;
    @Autowired
    private FileOpsService fileOpsServiceImpl;
    @Autowired
    private RabbitService rabbitService;
    @Autowired
    private StringRedisTemplate redisTemplate;

    // 分页查询用户专辑列表
    @Override
    public IPage<AlbumListVo> findUserAlbumPage(IPage<AlbumListVo> albumListVoPage, AlbumInfoQuery albumInfoQuery) {
        return albumInfoMapper.findUserAlbumPage(albumListVoPage, albumInfoQuery);
    }

    // 保存专辑信息
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveAlbumInfo(AlbumInfoVo albumInfoVo) {

        // 获取当前AOP代理对象，用于在目标类内部调用自身的方法时触发AOP代理
        AlbumInfoServiceImpl proxy = (AlbumInfoServiceImpl) AopContext.currentProxy();
        // 1.基本信息表保存数据（album_info）
        AlbumInfo albumInfo = new AlbumInfo();
        BeanUtils.copyProperties(albumInfoVo, albumInfo);
        Long userId = AuthContextHolder.getUserId();
        albumInfo.setUserId(userId);
        albumInfo.setStatus(SystemConstant.ALBUM_STATUS_PASS);
        if (!albumInfoVo.getPayType().equals(SystemConstant.ALBUM_PAY_TYPE_FREE)) {
            albumInfo.setTracksForFree(5);
        }
        albumInfoMapper.insert(albumInfo);
        // 2.像专辑属性值表保存数据（album_attr_value）
        List<AlbumAttributeValueVo> attributeValueVoList = albumInfoVo.getAlbumAttributeValueVoList();
        if (!CollectionUtils.isEmpty(attributeValueVoList)) {
            attributeValueVoList.forEach(albumAttributeValueVo -> {
                AlbumAttributeValue albumAttributeValue = new AlbumAttributeValue();
                albumAttributeValue.setAlbumId(albumInfo.getId());
                albumAttributeValue.setAttributeId(albumAttributeValueVo.getAttributeId());
                albumAttributeValue.setValueId(albumAttributeValueVo.getValueId());
                albumAttributeValueMapper.insert(albumAttributeValue);
            });
        }
        // 3.初始化专辑统计表 （album_stat为专辑的四个统计维度赋初始值）
        proxy.extracted(albumInfo.getId());
        // 4.将专辑信息保存到es中(sendMsg)
        if (albumInfoVo.getIsOpen().equals("1")) {
            rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_UPPER, albumInfo.getId().toString());
            log.info("专辑微服务发送消息到搜索微服务指定的队列，完成专辑：{}上架成功：", albumInfo.getId());
        }
    }

    // 3.初始化专辑统计表(方法抽取)
    @Transactional
    public void extracted(Long albumId) {
        Arrays.asList(SystemConstant.ALBUM_STAT_PLAY,
                SystemConstant.ALBUM_STAT_SUBSCRIBE,
                SystemConstant.ALBUM_STAT_BROWSE,
                SystemConstant.ALBUM_STAT_COMMENT).forEach(albumStatForm -> {
            AlbumStat albumStat = new AlbumStat();
            albumStat.setAlbumId(albumId);
            albumStat.setStatType(albumStatForm);
            albumStat.setStatNum(0);
            albumStatMapper.insert(albumStat);
        });
    }

    // 查询专辑信息（album_info）和专辑属性值表（album_attr_value）
    @Override
    public AlbumInfo getAlbumInfo(Long albumId) {
        AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
        if (albumInfo == null) {
            throw new GuiguException(201, "专辑不存在");
        }
        LambdaQueryWrapper<AlbumAttributeValue> wrapper = new LambdaQueryWrapper<AlbumAttributeValue>().eq(AlbumAttributeValue::getAlbumId, albumId);
        List<AlbumAttributeValue> attributeValueList = albumAttributeValueMapper.selectList(wrapper);
        albumInfo.setAlbumAttributeValueVoList(attributeValueList);
        return albumInfo;
    }

    // 删除专辑信息（album_info）和专辑属性值表（album_attr_value）以及专辑统计表(album_stat)
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void removeAlbumInfo(Long albumId) {
        AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
        if (albumInfo == null) {
            throw new GuiguException(201, "专辑不存在");
        }
        // https://imagev2.xmcdn.com/storages/bccd-audiofreehighqps/99/D4/CKwRINsFICW-AA2zdwDlXJeI.jpg
        String coverUrl = albumInfo.getCoverUrl();
        String originalFilename = coverUrl.substring(coverUrl.lastIndexOf("/"));
        fileOpsServiceImpl.minioDeleteExample(originalFilename);

        LambdaQueryWrapper<TrackInfo> wrapper = new LambdaQueryWrapper<TrackInfo>().eq(TrackInfo::getAlbumId, albumId);
        Long count = trackInfoMapper.selectCount(wrapper);
        if (count > 0) {
            throw new GuiguException(201, "专辑下有声音,请先删除声音");
        }
        // 先删除缓存再删除数据库数据
        String cacheKey = RedisConstant.CACHE_INFO_PREFIX + albumId;
        redisTemplate.delete(cacheKey);

        // 删除数据库数据
        albumInfoMapper.deleteById(albumInfo);

        LambdaQueryWrapper<AlbumAttributeValue> valueLambdaQueryWrapper = new LambdaQueryWrapper<AlbumAttributeValue>().eq(AlbumAttributeValue::getAlbumId, albumId);
        albumAttributeValueMapper.delete(valueLambdaQueryWrapper);

        LambdaQueryWrapper<AlbumStat> statLambdaQueryWrapper = new LambdaQueryWrapper<AlbumStat>().eq(AlbumStat::getAlbumId, albumId);
        albumStatMapper.delete(statLambdaQueryWrapper);

        // 延迟删除缓存
        ScheduleTaskThreadFactory.getInstance()
                .execute(() -> redisTemplate.
                                delete(cacheKey),
                        600L,
                        TimeUnit.MILLISECONDS);

        // 从es中删除专辑
        rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_LOWER, albumId.toString());
        log.info("专辑微服务发送消息到搜索微服务指定的队列，完成专辑：{}下架成功：", albumId);
    }

    // 更新专辑信息（album_info）和专辑属性值表（album_attr_value）
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateAlbumInfo(Long albumId, AlbumInfoVo albumInfoVo) {
        AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
        if (albumInfo == null) {
            throw new GuiguException(201, "专辑不存在");
        }

        //判断是否修改图片
        String coverUrl = albumInfoVo.getCoverUrl();
        if (StringUtils.hasText(coverUrl)) {
            String albumInfoCoverUrl = albumInfo.getCoverUrl();
            String originalFilename = albumInfoCoverUrl.substring(albumInfoCoverUrl.lastIndexOf("/"), albumInfoCoverUrl.length());
            fileOpsServiceImpl.minioDeleteExample(originalFilename);
        }

        BeanUtils.copyProperties(albumInfoVo, albumInfo);
        if (albumInfoVo.getPayType().equals(SystemConstant.ALBUM_PAY_TYPE_FREE)) {
            albumInfo.setPrice(new BigDecimal(0));
            albumInfo.setDiscount(new BigDecimal(-1));
            albumInfo.setVipDiscount(new BigDecimal(-1));
            albumInfo.setTracksForFree(0);
        } else {
            albumInfo.setTracksForFree(5);
        }


        /*
          重复尝试删除是为了防止删除缓存时出现失败
          先删除缓存再更新数据库数据(需要延迟双删+重复尝试删除)
          因为删除缓存前有另一个线程在删除前读取会在将旧数据存到缓存中.污染缓存
         */
        String cacheKey = RedisConstant.CACHE_INFO_PREFIX + albumId;
        redisTemplate.delete(cacheKey);

        // 更新专辑信息（album_info）
        albumInfoMapper.updateById(albumInfo);

        /*
          先更新数据库数据再删除缓存(需要重复尝试删除)
          因为先更新数据库数据,另一个线程在删除缓存读取只会有一次脏读,
          然后删除缓存后有其他线程在读取,就是新的一致的数据存在缓存中
//        String cacheKey = RedisConstant.CACHE_INFO_PREFIX + albumId;
//        redisTemplate.delete(cacheKey);
         */


        // 延迟删除缓存
        ScheduleTaskThreadFactory.getInstance()
                .execute(() -> redisTemplate.
                                delete(cacheKey),
                        600L,
                        TimeUnit.MILLISECONDS);

        // 删除专辑属性值表（album_attr_value）
        albumAttributeValueMapper.delete(new LambdaQueryWrapper<AlbumAttributeValue>().eq(AlbumAttributeValue::getAlbumId, albumId));
        List<AlbumAttributeValueVo> attributeValueVoList = albumInfoVo.getAlbumAttributeValueVoList();
        List<AlbumAttributeValue> attributeValueList = attributeValueVoList.stream().map(albumAttributeValueVo -> {
            AlbumAttributeValue albumAttributeValue = new AlbumAttributeValue();
            albumAttributeValue.setAlbumId(albumId);
            albumAttributeValue.setValueId(albumAttributeValueVo.getValueId());
            albumAttributeValue.setAttributeId(albumAttributeValueVo.getAttributeId());
            return albumAttributeValue;
        }).collect(Collectors.toList());

        // 保存专辑属性值表（album_attr_value）
        if (!CollectionUtils.isEmpty(attributeValueList)) {
            albumAttributeValueService.saveBatch(attributeValueList);
        }

        // 判断是否上架
        if (albumInfoVo.getIsOpen().equals("1")) {
            rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_UPPER, albumInfo.getId().toString());
            log.info("专辑微服务发送消息到搜索微服务指定的队列，完成专辑：{}上架成功：", albumInfo.getId());
        } else {
            rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_LOWER, albumId.toString());
            log.info("专辑微服务发送消息到搜索微服务指定的队列，完成专辑：{}下架成功：", albumId);
        }
    }

    // 查询用户所有专辑列表
    @Override
    public List<AlbumInfo> findUserAllAlbumList(Long userId) {
        LambdaQueryWrapper<AlbumInfo> albumInfoLambdaQueryWrapper = new LambdaQueryWrapper<AlbumInfo>().eq(AlbumInfo::getUserId, userId);
        return albumInfoMapper.selectList(albumInfoLambdaQueryWrapper);
    }

    @Override
    public AlbumStatVo getAlbumInfoStat(Long albumId) {
        return albumStatMapper.getAlbumInfoStat(albumId);
    }

    @Override
    public List<Long> getAlbumInfoIdList(Long userId) {
        List<AlbumInfo> userAllAlbumList = findUserAllAlbumList(userId);
        return userAllAlbumList.stream().map(AlbumInfo::getId).collect(Collectors.toList());
    }

    @Override
    public List<Long> getAlbumIds() {
        List<AlbumInfo> albumInfoList = albumInfoMapper.selectList(new LambdaQueryWrapper<AlbumInfo>().select(AlbumInfo::getId));
        return albumInfoList.stream().map(AlbumInfo::getId).collect(Collectors.toList());
    }

}
